﻿using System;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

namespace Framework
{
    public class ResKit
    {
        private static ResKit _instance;
        public static ResKit Instance => _instance ??= new ResKit();

        public async Task<T> LoadAssetAsync<T>(MonoBehaviour target, string address, Action<T> completed = null) where T : class
        {
            var source = new TaskCompletionSource<T>();
            var handle = LoadAsset(target, address, (T result) =>
            {
                source.TrySetResult(result);
                completed?.Invoke(result);
            });
            await source.Task;
            return handle.Status == AsyncOperationStatus.Succeeded ? handle.Result : null;
        }

        public Task<T> LoadAssetAsync<T>(MonoBehaviour target, AssetReference assetReference, Action<T> completed = null) where T : class
        {
            return LoadAssetAsync(target, assetReference.AssetGUID, completed);
        }

        public AsyncOperationHandle<T> LoadAsset<T>(MonoBehaviour target, string address, Action<T> completed)
        {
            var isTargetValid = true;
            var handle = Addressables.LoadAssetAsync<T>(address);
            handle.Completed += _ =>
            {
                if (isTargetValid)
                {
                    completed?.Invoke(handle.Result);
                }
                else
                {
                    Debug.LogWarning($"ResKit - Target destroyed. Address: {address}");
                }
            };

            var lifecycleBehaviour = target.GetOrAddComponent<LifecycleBehaviour>();
            lifecycleBehaviour.OnDestroyEvent += () => isTargetValid = false;
            return handle;
        }

        public AsyncOperationHandle<T> LoadAsset<T>(MonoBehaviour target, AssetReference assetReference, Action<T> completed)
        {
            return LoadAsset(target, assetReference.AssetGUID, completed);
        }

        public void LoadScene(string address)
        {
            Addressables.LoadSceneAsync(address);
        }

        public Task<T> LoadAssetReference<T>(MonoBehaviour target, AssetReference assetReference) where T : class
        {
            return LoadAssetAsync<T>(target, assetReference.AssetGUID);
        }
    }
}